import cv2 as cv
#定义提取特征点与光流估计参数，字典
feature_params=dict(maxCorners=100,qualityLevel=0.3,minDistance=7,blockSize=7)
lk_params=dict(winSize=(15,15),maxLevel=2,criteria=(cv.TERM_CRITERIA_EPS|cv.TERM_CRITERIA_COUNT,10,0.03))


cap = cv.VideoCapture(r'vtest.avi')

ret, prev = cap.read()
prevgray = cv.cvtColor(prev, cv.COLOR_BGR2GRAY)
p0 = cv.goodFeaturesToTrack(prevgray, mask=None, **feature_params)
#先计算出第一帧的特征点
#*list可以将列表与字典的key取出，**dict可以取出字典中的值

while True:
    ret, frame = cap.read()
    if not ret:
        break
    gray=cv.cvtColor(frame,cv.COLOR_BGR2GRAY)
    #应用L-K光流估计，先前帧图像与特征点和当前帧图像
    #返回三个参数：第一个光流点的列表；第二个点的置信度，越大越有效，（0,1）；第三个错误
    p1,st,err=cv.calcOpticalFlowPyrLK(prevgray,gray,p0,None,**lk_params)

    goodPoints=p1[st==1]
    goodPrevPoints=p0[st==1]


    res=frame.copy()
    drawColor=(0,0,255)
    #在结果图像上绘制出特征点与光流向量
#enumerate(sequence, [start=0]) 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列，同时列出数据和数据下标，一般用在 for 循环当中,如下示例
#    >> > seasons = ['Spring', 'Summer', 'Fall', 'Winter']
#    >> > list(enumerate(seasons))
#    [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
#zip([iterable, ...]) 函数用于将可迭代的对象作为参数，将对象中对应的元素打包成一个个元组，然后返回由这些元组组成的列表。
    for i,(cur,prev) in enumerate(zip(goodPoints,goodPrevPoints)):
        x0,y0=cur.ravel()
        x1,y1=prev.ravel()
        cv.line(res,(x0,y0),(x1,y1),drawColor)
        cv.circle(res,(x0,y0),3,drawColor)

    #更新
    prevgray=gray.copy()
    p0=goodPoints.reshape(-1,1,2)

    cv.imshow('result',res)

    key = cv.waitKey(30)
    if key ==27:
        break

cap.release()
cv.destroyAllWindows()